﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate;
using BenevolentSun;
using BenevolentSunData;

namespace Editor
{
    public class LevelEditorScreen : Screen
    {

        #region State
        Vector2 cameraVeloc = new Vector2();

        string levelSource;

        Level level;
        #endregion

        
        #region Initialization
        public LevelEditorScreen(string levelSource) {
            this.levelSource = levelSource;
        }

        public override void LoadContent()
        {
            Map map = Content.Load<Map>(levelSource);
            map.LoadContent(Content, GraphicsDevice);

            level = new Level(map, Content, GraphicsDevice);

            if (level.Map.MusicCueName != null && level.Map.MusicCueName != AudioManager.CurrentMusic())
                AudioManager.PlayMusic(level.Map.MusicCueName);

            base.LoadContent();
        }
        #endregion


        #region Editing
        private void NextCellType()
        {
            if (level.SelectedCell is NullCell)       // null--> empty
            {
                EmptyCell cell = new EmptyCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
            else if (level.SelectedCell is EmptyCell) // empty --> wall
            {
                WallCell cell = new WallCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
            else if (level.SelectedCell is WallCell)  // wall --> source
            {
                SourceCell cell = new SourceCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
            else if (level.SelectedCell is SourceCell)    // source --> hue
            {
                HueCell cell = new HueCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
            else if (level.SelectedCell is HueCell)   // hue --> prism
            {
                PrismCell cell = new PrismCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
            else if (level.SelectedCell is PrismCell) // prism --> mirror
            {
                MirrorCell cell = new MirrorCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
            else if (level.SelectedCell is MirrorCell)    // mirrio --> sink
            {
                SinkCell cell = new SinkCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
            else if (level.SelectedCell is SinkCell)  // sink --> torch
            {
                BulbCell cell = new BulbCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
            else if (level.SelectedCell is BulbCell)  // torch --> null
            {
                NullCell cell = new NullCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
        }

        private void PrevCellType()
        {
            if (level.SelectedCell is EmptyCell)       // null <-- empty
            {
                NullCell cell = new NullCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
            else if (level.SelectedCell is WallCell) // empty <-- wall
            {
                EmptyCell cell = new EmptyCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
            else if (level.SelectedCell is SourceCell)  // wall <-- source
            {
                WallCell cell = new WallCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
            else if (level.SelectedCell is HueCell)    // source <-- hue
            {
                SourceCell cell = new SourceCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
            else if (level.SelectedCell is PrismCell)   // hue <-- prism
            {
                HueCell cell = new HueCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
            else if (level.SelectedCell is MirrorCell) // prism <-- mirror
            {
                PrismCell cell = new PrismCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
            else if (level.SelectedCell is SinkCell)    // mirror <-- sink
            {
                MirrorCell cell = new MirrorCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
            else if (level.SelectedCell is BulbCell)  // sink <-- torch
            {
                SinkCell cell = new SinkCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
            else if (level.SelectedCell is NullCell)  // torch <-- null
            {
                BulbCell cell = new BulbCell();
                cell.LoadContent(Content, level.Map);
                cell.Coords = level.SelectedCell.Coords;
                level.Map.SetCell(cell);
                level.SelectedCell = cell;
                level.Map.InitBeams();
            }
        }
        #endregion


        #region Input
        public override void HandleInput()
        {

            cameraVeloc = ActionMap.CameraMovement();

            level.MoveSelector(ActionMap.SelectorMovement());

            #region Interaction
            if (ActionMap.CellInteraction())
            {
                level.Interact();
            }

            if (InputManager.IsNewButtonPress(PlayerIndex.One, Buttons.B)
                || InputManager.IsNewKeyPress(Keys.B))
            {
                #region Hue
                if (level.SelectedCell is HueCell)
                {
                    HueCell hCell = level.SelectedCell as HueCell;

                    if (hCell.Hue == Color.Red)
                    {
                        hCell.Hue = Color.Lime;
                    }
                    else if (hCell.Hue.Equals(Color.Lime))
                    {
                        hCell.Hue = Color.Blue;
                    }
                    else if (hCell.Hue == Color.Blue)
                    {
                        hCell.Hue = Color.Yellow;
                    }
                    else if (hCell.Hue == Color.Yellow)
                    {
                        hCell.Hue = Color.Fuchsia;
                    }
                    else if (hCell.Hue == Color.Fuchsia)
                    {
                        hCell.Hue = Color.Aqua;
                    }
                    else if (hCell.Hue == Color.Aqua)
                    {
                        hCell.Hue = Color.Red;
                    }

                    level.Map.InitBeams();
                }
                #endregion

                #region Source
                if (level.SelectedCell is SourceCell)
                {
                    SourceCell hCell = level.SelectedCell as SourceCell;

                    if (hCell.BeamColor == Color.White)
                    {
                        hCell.BeamColor = Color.Red;
                    }
                    else if (hCell.BeamColor == Color.Red)
                    {
                        hCell.BeamColor = Color.Lime;
                    }
                    else if (hCell.BeamColor.Equals(Color.Lime))
                    {
                        hCell.BeamColor = Color.Blue;
                    }
                    else if (hCell.BeamColor == Color.Blue)
                    {
                        hCell.BeamColor = Color.Yellow;
                    }
                    else if (hCell.BeamColor == Color.Yellow)
                    {
                        hCell.BeamColor = Color.Fuchsia;
                    }
                    else if (hCell.BeamColor == Color.Fuchsia)
                    {
                        hCell.BeamColor = Color.Aqua;
                    }
                    else if (hCell.BeamColor == Color.Aqua)
                    {
                        hCell.BeamColor = Color.White;
                    }

                    level.Map.InitBeams();
                }
                #endregion
                
                #region Sink
                if (level.SelectedCell is SinkCell)
                {
                    SinkCell hCell = level.SelectedCell as SinkCell;

                    if (hCell.RequiredColor == Color.White)
                    {
                        hCell.RequiredColor = Color.Red;
                    }
                    else if (hCell.RequiredColor == Color.Red)
                    {
                        hCell.RequiredColor = Color.Lime;
                    }
                    else if (hCell.RequiredColor.Equals(Color.Lime))
                    {
                        hCell.RequiredColor = Color.Blue;
                    }
                    else if (hCell.RequiredColor == Color.Blue)
                    {
                        hCell.RequiredColor = Color.Yellow;
                    }
                    else if (hCell.RequiredColor == Color.Yellow)
                    {
                        hCell.RequiredColor = Color.Fuchsia;
                    }
                    else if (hCell.RequiredColor == Color.Fuchsia)
                    {
                        hCell.RequiredColor = Color.Aqua;
                    }
                    else if (hCell.RequiredColor == Color.Aqua)
                    {
                        hCell.RequiredColor = Color.White;
                    }

                    level.Map.InitBeams();
                }
                #endregion
            }

            if (InputManager.IsNewButtonPress(PlayerIndex.One, Buttons.Y)
                || InputManager.IsNewKeyPress(Keys.Y))
            {
                if (level.SelectedCell is HueCell)
                {
                    HueCell hCell = level.SelectedCell as HueCell;
                    hCell.HasWalls = !hCell.HasWalls;
                    level.Map.InitBeams();
                }
            }
            #endregion

            #region Cell Type
            if (InputManager.IsNewButtonPress(PlayerIndex.One, Buttons.LeftShoulder) ||
                InputManager.IsNewKeyPress(Keys.Q))
            {
                NextCellType();  
            }
            if (InputManager.IsNewButtonPress(PlayerIndex.One, Buttons.RightShoulder) || 
                InputManager.IsNewKeyPress(Keys.E))
            {
                PrevCellType();
            }
            #endregion

            #region Rotation
            if (level.SelectedCell is HueCell)
            {
                if (InputManager.IsNewButtonPress(PlayerIndex.One, Buttons.RightThumbstickDown)
                    || InputManager.IsNewKeyPress(Keys.K))
                {
                    ((HueCell)level.SelectedCell).Orientation = Orientation.NorthSouth;
                    level.Map.InitBeams();
                }
                if (InputManager.IsNewButtonPress(PlayerIndex.One, Buttons.RightThumbstickUp)
                    || InputManager.IsNewKeyPress(Keys.I))
                {
                    ((HueCell)level.SelectedCell).Orientation = Orientation.NorthSouth;
                    level.Map.InitBeams();
                }
                if (InputManager.IsNewButtonPress(PlayerIndex.One, Buttons.RightThumbstickLeft)
                    || InputManager.IsNewKeyPress(Keys.J))
                {
                    ((HueCell)level.SelectedCell).Orientation = Orientation.EastWest;
                    level.Map.InitBeams();
                }
                if (InputManager.IsNewButtonPress(PlayerIndex.One, Buttons.RightThumbstickRight)
                    || InputManager.IsNewKeyPress(Keys.L))
                {
                    ((HueCell)level.SelectedCell).Orientation = Orientation.EastWest;
                    level.Map.InitBeams();
                }
            }

            if (level.SelectedCell is SourceCell)
            {
                if (InputManager.IsNewButtonPress(PlayerIndex.One, Buttons.RightThumbstickDown)
                    ||InputManager.IsNewKeyPress(Keys.K))
                {
                    ((SourceCell)level.SelectedCell).BeamDirection = Direction.South;
                    level.Map.InitBeams();
                }
                if (InputManager.IsNewButtonPress(PlayerIndex.One, Buttons.RightThumbstickUp)
                    || InputManager.IsNewKeyPress(Keys.I))
                {
                    ((SourceCell)level.SelectedCell).BeamDirection = Direction.North;
                    level.Map.InitBeams();
                }
                if (InputManager.IsNewButtonPress(PlayerIndex.One, Buttons.RightThumbstickLeft)
                    || InputManager.IsNewKeyPress(Keys.J))
                {
                    ((SourceCell)level.SelectedCell).BeamDirection = Direction.West;
                    level.Map.InitBeams();
                }
                if (InputManager.IsNewButtonPress(PlayerIndex.One, Buttons.RightThumbstickRight)
                    || InputManager.IsNewKeyPress(Keys.L))
                {
                    ((SourceCell)level.SelectedCell).BeamDirection = Direction.East;
                    level.Map.InitBeams();
                }
            }
            
            if (level.SelectedCell is SinkCell)
            {
                if (InputManager.IsNewButtonPress(PlayerIndex.One, Buttons.RightThumbstickDown)
                    || InputManager.IsNewKeyPress(Keys.K))
                {
                    ((SinkCell)level.SelectedCell).EntranceDirection = Direction.South;
                    level.Map.InitBeams();
                }
                if (InputManager.IsNewButtonPress(PlayerIndex.One, Buttons.RightThumbstickUp)
                    || InputManager.IsNewKeyPress(Keys.I))
                {
                    ((SinkCell)level.SelectedCell).EntranceDirection = Direction.North;
                    level.Map.InitBeams();
                }
                if (InputManager.IsNewButtonPress(PlayerIndex.One, Buttons.RightThumbstickLeft)
                    || InputManager.IsNewKeyPress(Keys.J))
                {
                    ((SinkCell)level.SelectedCell).EntranceDirection = Direction.West;
                    level.Map.InitBeams();
                }
                if (InputManager.IsNewButtonPress(PlayerIndex.One, Buttons.RightThumbstickRight)
                    || InputManager.IsNewKeyPress(Keys.L))
                {
                    ((SinkCell)level.SelectedCell).EntranceDirection = Direction.East;
                    level.Map.InitBeams();
                }
            }
            #endregion

            #region Menu
            if (InputManager.IsButtonPress(PlayerIndex.One, Buttons.Start)
                || InputManager.IsNewKeyPress(Keys.Escape))
            {
                SaveConfirmScreen scs = new SaveConfirmScreen();
                scs.OnSave += new EventHandler(scs_OnSave);
                scs.OnSaveAndClose += new EventHandler(scs_OnSaveAndClose);
                ScreenManager.AddScreen(scs);
            }
            #endregion

            base.HandleInput();
        }

        void scs_OnSaveAndClose(object sender, EventArgs e)
        {
            SaveMap();
            ScreenManager.AddScreen(new LevelMenuScreen());
            ExitScreen();
        }

        void scs_OnSave(object sender, EventArgs e)
        {
            SaveMap();
        }
        #endregion


        #region Saving Map To File
        public void SaveMap()
        {
            // retrieve the storage device, asynchronously
            GetStorageDevice(delegate(StorageDevice storageDevice)
            {
                SaveMapResult(storageDevice);
            });
        }

        private void SaveMapResult(StorageDevice storageDevice)
        {
            // check the parameter
            if ((storageDevice == null) || !storageDevice.IsConnected)
            {
                return;
            }

            // open the container
            using (StorageContainer storageContainer = storageDevice.OpenContainer("BSunSavedMaps"))
            {
                string filename;

                // get the filenames
                filename = Editor.GetLevelSourceFile(levelSource);
                //Path.Combine(storageContainer.Path, "TestSavedMap");
                //System.Reflection.Assembly.GetEntryAssembly().GetModules()[0].FullyQualifiedName;


                using (FileStream stream = new FileStream(filename, FileMode.Create))
                {

                    using (XmlWriter xmlWriter = XmlWriter.Create(stream))
                    {
                        IntermediateSerializer.Serialize<Map>(xmlWriter, level.Map, "dur_relative");
                    }
                }
            }
        }


        private StorageDevice storageDevice;

        /// <summary>
        /// A delegate for receiving StorageDevice objects.
        /// </summary>
        public delegate void StorageDeviceDelegate(StorageDevice storageDevice);

        public void GetStorageDevice(StorageDeviceDelegate retrievalDelegate)
        {
            // check the parameter
            if (retrievalDelegate == null)
            {
                throw new ArgumentNullException("retrievalDelegate");
            }

            // check the stored storage device
            if ((storageDevice != null) && storageDevice.IsConnected)
            {
                retrievalDelegate(storageDevice);
                return;
            }

            // the storage device must be retrieved
            storageDevice = null;
            Guide.BeginShowStorageDeviceSelector(GetStorageDeviceResult,
                retrievalDelegate);
        }

        private void GetStorageDeviceResult(IAsyncResult result)
        {
            // check the parameter
            if ((result == null) || !result.IsCompleted)
            {
                return;
            }

            // retrieve and store the storage device
            storageDevice = Guide.EndShowStorageDeviceSelector(result);

            // check the new storage device 
            if ((storageDevice != null) && storageDevice.IsConnected)
            {
                // it passes; call the stored delegate
                StorageDeviceDelegate func = result.AsyncState as StorageDeviceDelegate;
                if (func != null)
                {
                    func(storageDevice);
                }
            }
        }
        #endregion


        #region Updating
        public override void Update(GameTime gameTime, bool isFocusAvailable, bool isOccluded)
        {
            // Update our camera
            if (isFocusAvailable)
            {
                if (cameraVeloc.LengthSquared() != 0)
                {
                    level.MoveViewConstrained(cameraVeloc * 350f * (float)gameTime.ElapsedGameTime.TotalSeconds);
                    level.SelectCellAt(level.Camera.At);
                }
                level.Update(gameTime);
            }

            base.Update(gameTime, isFocusAvailable, isOccluded);
        }
        #endregion


        #region Drawing
        public override void Draw(GameTime gameTime)
        {
            level.Draw(gameTime);
            base.Draw(gameTime);
        }
        #endregion

    }

}
